//
// Manages drag and drop between several Widget objects which are hosted inside iframes
// inside a main container (for instance a Portal)
//
// USAGE 
//
// a. imports this file, it creates a dragManager object in the global scope
// b. call dragManager.initialize("ghostID", "maskID", isInPortal) 
//    in the document onload handler 
//
// The isInPortal option is required (default to false) to call the correct jQuery functions
// to get the geometry of widget frames and that depend of the version of jQuery in use 
// by the portal
//
// WidgetDragStub.js refers to the global "dragManager" object, so if you change its name
// (last line of this file) you need to update WidgetDragStub.js accordingly
//
// TO DO 
//
// - manage widget destruction or complete widget unsubscribing
//   that implies that removeWidgetEventListener from the Widget object 
//   should call a kind of unbindWidgetFromDropType to be defined
//
// NOTE
//
// This code depends on jQuery

function WidgetDragProxy (widgetID, widgetObject, isInsidePortal) {
	if (isInsidePortal) {
		this.iframeSelector = "#iframe_" + widgetID;
		this.containerSelector = "#wdg_" + widgetID;		
	} else {
		this.iframeSelector = "#" + widgetID;		
	}
	this.iframeElement = $(this.iframeSelector).get(0);	
	this.iframeWidget = widgetObject;
	this.acceptedTypes = {};	
	this.minX = this.minY = this.maxX = this.maxY = 0;
}	

WidgetDragProxy.prototype = {
	
	offset : function (isInsidePortal) {
		if (isInsidePortal) {
			var pos = jQuery.iUtil.getPosition(this.iframeElement);
			return { left : pos.x, top : pos.y };
		}
		return $(this.iframeSelector).offset();
	},
			
	saveBoundingBox : function (isInsidePortal) {		
		var pos, h, w;
		if (isInsidePortal) {
			pos = this.offset (isInsidePortal);
			var size = jQuery.iUtil.getSize(this.iframeElement);
			h = size.hb;
			w = size.wb;
		} else {
			pos = $(this.iframeSelector).offset();
			h = $(this.iframeSelector).outerHeight();
			w = $(this.iframeSelector).outerWidth();
		}
		this.minX = pos.left;
		this.minY = pos.top;
		this.maxX = pos.left + w;
		this.maxY = pos.top + h;	
		// console.log("saveBoundingBox " + this.iframeSelector + "," + this.minX + "," + this.minY + "," + this.maxX + "," + this.maxY);
	},
		
	contains : function (x, y) {		
		if ((x > this.minX) && (x < this.maxX) && (y > this.minY)  && (y < this.maxY)) {
			return true;
		} else {
			return false;
		}
	},
	
	displayDropFeedback : function () {
		$(this.containerSelector).addClass('drop_target');
	},
	
	hideDropFeedback : function () {
		$(this.containerSelector).removeClass('drop_target');	
	},	
	
	// Configure widget so that it responds to drag event with type 'type'
	bindWidgetToDropType : function (type) 	{	
		this.acceptedTypes[type] = true;
	},
	
	acceptDragData : function (type) {
		return (this.acceptedTypes[type] == true);
	},
	
	dropEvent : function (target, type, data) {
		$(this.containerSelector).addClass('drop_dropped');		
		var mgr = this; // closure
		window.setTimeout (function () { $(mgr.containerSelector).removeClass('drop_dropped');		 }, 1000);

		// dispatches event
		this.iframeWidget.fireWidgetEvent(target, type, data);
	}
	
	// function setDragData (element, tooltip, URI)  ==> mousedown handler qui appelle startDrag du parent
	// acceptDrop (URI) : true ou false, appelé par le DragManager
	// dispatchDropEvent ==> appelle le bon callback
	// addEventListener("uriDropEvent")  => appelle dispatchDropEvent
};

// Should be a singletong
function DragManager () {
	this.widgets = [];
	var mgr = this; // closure
	this._doDragCb = function (ev) { mgr._doDrag(ev); return false;};
	this._doEndDragCb = function (ev) { mgr._doEndDrag(ev); return false;};
}

DragManager.prototype = {
		
	initialize : function (ghostID, maskID, inPortal) {
		this.ghostSelector = "#" + ghostID; // jQuery selector
		this.maskSelector = "#" + maskID; // jQuery selector
		this.ghostElement = $(this.ghostSelector).get(0);		
		this.isInsidePortal = inPortal || false;
	},
		
	registerWidget : function (widgetId, widgetObject) {	
		var widget = new WidgetDragProxy (widgetId, widgetObject, this.isInsidePortal);
		this.widgets.push( [widget, widgetId] );
	},
	
	bindWidgetToDropType : function (widgetID, type) 	{
		for (var i = 0; i < this.widgets.length; i++) {
			if (this.widgets[i][1] == widgetID) {
				//console.log('Bind Widget ' + widgetID + ' to ' + type);
				this.widgets[i][0].bindWidgetToDropType (type);
				break;				
			}
		}
	},
	
	unregisterWidget : function (id) {
		// TBD
	},
	
	displayGhost : function (message) {
		$(this.ghostSelector).css('visibility','visible');
		$(this.ghostSelector).text( message || '' );
	},
	
	moveGhostTo : function (x, y) {
		this.ghostElement.style.left = x + 'px';
		this.ghostElement.style.top = y + 'px';
	},
	
	displayMask : function () {	
		$(this.maskSelector).css('visibility', 'visible');
		$(this.maskSelector).css('opacity', '0.1');				
		$(this.maskSelector).css('background-color', '#ddd');		
	},
	
	hideMask : function () {		
		$(this.maskSelector).css('visibility', 'hidden');
		$(this.maskSelector).css('opacity', '0');		
	},
	
	hideGhost : function () {
		$(this.ghostSelector).css('visibility','hidden');
		$(this.ghostSelector).text('');						
	},
	
	// Returns the index of the widget that contains (x, y) or -1 if none
	peekWidget : function (x, y) {
		for (var i = 0; i < this.widgets.length; i++) {
			if (this.widgets[i][0].contains(x, y) && this.widgets[i][0].acceptDragData(this.dragEventData.dropEventType)) {
				return i;
			}
		}
		return -1;
	},	
	
	trackWidget : function (x, y) {
		var cur = this.peekWidget(x,y);
		if (cur == this.origin) { // origin widget is not a potential target
			cur = -1;
		}
		if (cur != this.peeked) {
			// console.log("Peek widged changed from " + this.peeked + " to " + cur);
			if (this.peeked == undefined) {
				//console.log("in DragManager.trackWidget : UNDEFINED !");
			} else {
				if (this.peeked != -1) {
					this.widgets[this.peeked][0].hideDropFeedback ();
				}
			}	 
			if (cur != -1) {
				this.widgets[cur][0].displayDropFeedback ();
			}
			this.peeked = cur;
		}
	},
	
	startDrag : function (x, y, frameID, type, data, message) {
		
		// calc origin widget
		// a better method would be to use pointer X, Y, but currently we just have iframe clientX clientY...
		this.origin = -1; // unknown
		this.dragEventData = { dropEventType : type, dropEventData : data };		
		for (var i = 0; i < this.widgets.length; i++) {
			if (this.widgets[i][1] == frameID) {
				this.origin = i;
			}
		}
		if (this.origin != -1)	{
			var offset = this.widgets[this.origin][0].offset (this.isInsidePortal);
			// console.log("Offset " + offset.left + "," + offset.top);
			this._doStartDrag( {clientX : x + offset.left, clientY : y + offset.top}, message); // NB: ajouter offset de la frame - scroll
		}
		return false;
	},
	
	// pre-condition : set this.origin to index of drag source widget
	_doStartDrag : function  (ev, message) {	
		
		for (var i = 0; i < this.widgets.length; i++) {
			this.widgets[i][0].saveBoundingBox (this.isInsidePortal);
		}
		
		this.peeked = -1;		
		this.moveGhostTo(ev.clientX, ev.clientY);
		
		this.displayGhost (message);
		
		this.displayMask ();

		$(this.maskSelector).mousemove(this._doDragCb);		
		$(this.maskSelector).mouseup(this._doEndDragCb); // maybe $(this.maskSelector) better than "body" ?
	
	},
	
	_doDrag : function (ev) {
			
		this.trackWidget (ev.pageX, ev.pageY);		
		this.moveGhostTo (ev.clientX, ev.clientY);
		
		ev.returnValue = false;
		ev.cancelBubble = true;
		
		return false;
	},	

	_doEndDrag : function  (ev) {
		
		if (this.peeked != -1) {
			this.widgets[this.peeked][0].hideDropFeedback ();
		}
		this.peeked = this.peekWidget (ev.pageX, ev.pageY);
		this.hideGhost ();
		this.hideMask ();
		$(this.maskSelector).unbind('mouseup');
		$(this.maskSelector).unbind('mousemove');
		if ((-1 != this.peeked) && (this.origin != this.peeked)) {
			
			this.widgets[this.peeked][0].dropEvent (this.widgets[this.peeked][0].iframeWidget.id, this.dragEventData.dropEventType, this.dragEventData.dropEventData);
		}
		
		return false;
	}
}

//console.log('Creating drag manager');
var dragManager = new DragManager();
